﻿using System.Linq.Expressions;
using AutoMapper;
using CozeDomain.Entities;
using CozeDomain.IRepositories;
using CozeDomain.IServices;
using D.Util.UtilCache;
using D.Util.UtilConfiguration;
using D.UtilCore.Dtos;
using D.UtilCore.JwtUtil;
using D.UtilCore.Models;
using D.UtilFreesqlOrm;
using Microsoft.AspNetCore.Http;
using D.UtilCore.WChartUtils;
using CozeDto.AdminDtos;
using D.Util.UtilNLog;
using CozeDto.UserDtos;

namespace CozeDomain.Services
{
    /// <summary>
    /// 管理员业务实现
    /// </summary>
    public class SysAdminService : BaseService<SysAdminEntity>, ISysAdminService
    {
        private readonly IMapper _mapper;
        private readonly ICacheService _cache;
        private readonly IJwtService _tokenService;
        private readonly INLogService _nlogService;
        private readonly ISysAdminEntityRepository _repository;
        private readonly IHttpContextAccessor _httpContextAccessor;

        /// <summary>
        /// 构造注入
        /// </summary>
        public SysAdminService(IMapper mapper, ICacheService cache, IJwtService tokenService, INLogService nlogService, IHttpContextAccessor httpContextAccessor,
            ISysAdminEntityRepository repository)
        {
            // 用于base方法
            base._baseDal = repository;
            this._repository = repository;
            _cache = cache;
            _mapper = mapper;
            _nlogService = nlogService;
            _tokenService = tokenService;
            _httpContextAccessor = httpContextAccessor;
        }

        #region 增删改查

        /// <summary>
        /// 新增用户
        /// </summary>
        /// <param name="reqModel"></param>
        /// <returns></returns>
        public async Task<ApiResult<int>> AddAdminAsync(ReqAddAdmin reqModel)
        {
            var exists = await _repository.ExistsAsync(m => m.Account == reqModel.Account);
            if (exists)
            {
                return new ApiResult<int>() { Code = StatusCodeEnum.Waring, Msg = "账号已存在" };
            }

            var insertEntity = _mapper.Map<ReqAddAdmin, SysAdminEntity>(reqModel);
            insertEntity.TenantIds = string.Join(",", reqModel.TenantIds);
            insertEntity.DeleteFlag = false;
            insertEntity.CreateTime = DateTime.Now;
            insertEntity.Id = D.UtilFreesqlOrm.FreesqlOrm.NewId;
            insertEntity.LoginTotalCount = 0;
            insertEntity.Status = true;

            var dbres = await _repository.AddAsync(insertEntity);

            return dbres.NormalResponse();
        }

        /// <summary>
        /// 删除用户
        /// </summary>
        /// <param name="ids"></param>
        /// <returns></returns>
        public async Task<ApiResult<int>> DeleteAdminAsync(List<string> ids)
        {
            var dbres = await _repository.DeleteAsync(m => ids.Contains(m.Id));
            return dbres.NormalResponse();
        }

        /// <summary>
        /// 修改用户
        /// </summary>
        /// <param name="reqModel"></param>
        /// <returns></returns>
        public async Task<ApiResult<int>> UpdateAdminAsync(ReqUpdateAdmin reqModel)
        {
            var updateEntity = await _repository.GetModelAsync(m => m.Id == reqModel.Id);
            if (updateEntity == null)
            {
                return new ApiResult<int>() { Code = StatusCodeEnum.Waring, Msg = "该信息不存在" };
            }

            updateEntity.Status = reqModel.Status;
            updateEntity.Email = reqModel.Email;
            updateEntity.NickName = reqModel.NickName;
            updateEntity.WeChartAccount = reqModel.WeChartAccount;
            updateEntity.Phone = reqModel.Phone;
            updateEntity.TenantIds = string.Join(",", reqModel.TenantIds);
            updateEntity.Description = reqModel.Description;
            updateEntity.UpdateTime = DateTime.Now;
            updateEntity.UpdateUser = reqModel.UpdateUser;

            var dbres = await _repository.UpdateAsync(updateEntity);

            return dbres.NormalResponse();
        }

        /// <summary>
        /// 修改密码
        /// </summary>
        /// <param name="reqModel"></param>
        /// <returns></returns>
        public async Task<ApiResult<int>> UpdatePwdAsync(ReqUpdatePwd reqModel)
        {
            if (reqModel.Pwd != reqModel.PasswordConfirm)
            {
                return new ApiResult<int>() { Code = StatusCodeEnum.Waring, Msg = "俩次密码输入不一致" };
            }

            var updateEntity = await _repository.GetModelAsync(m => m.Id == reqModel.Id);
            if (updateEntity == null)
            {
                return new ApiResult<int>() { Code = StatusCodeEnum.Waring, Msg = "该用户信息不存在" };
            }

            updateEntity.Pwd = reqModel.Pwd;
            updateEntity.UpdateTime = DateTime.Now;
            updateEntity.UpdateUser = reqModel.UpdateUser;

            var dbres = await _repository.UpdateAsync(updateEntity);

            return dbres.NormalResponse();
        }

        /// <summary>
        /// 修改状态
        /// </summary>
        /// <param name="reqModel"></param>
        /// <returns></returns>
        public async Task<ApiResult<int>> ChangeStatusAsync(ReqChangeStatus reqModel)
        {
            var updateEntity = await _repository.GetModelAsync(m => m.Id == reqModel.Id);
            if (updateEntity == null)
            {
                return new ApiResult<int>() { Code = StatusCodeEnum.Waring, Msg = "该用户信息不存在" };
            }

            if (updateEntity.Status == reqModel.Status)
            {
                return new ApiResult<int>() { Code = StatusCodeEnum.Waring, Msg = "该用户状态信息已修改" };
            }

            updateEntity.Status = reqModel.Status;
            updateEntity.UpdateTime = DateTime.Now;
            updateEntity.UpdateUser = reqModel.UpdateUser;

            var dbres = await _repository.UpdateAsync(updateEntity);

            return dbres.NormalResponse();
        }


        /// <summary>
        /// 获取用户详情
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<ApiResult<RepAdminInfo>> GetAdminAsync(string id)
        {
            var dbres = await _repository.GetModelAsync(m => m.Id == id);
            var res = _mapper.Map<SysAdminEntity, RepAdminInfo>(dbres);
            res.TenantIds = dbres.TenantIds?.Split(",").ToList();
            return res.GetResponse();
        }

        /// <summary>
        /// 获取用户分页
        /// </summary>
        /// <param name="reqQueryAdminPage"></param>
        /// <returns></returns>
        public async Task<ApiResult<PageModel<RepAdminPageInfo>>> QueryAdminPagesAsync(ReqQueryAdminPage reqQueryAdminPage)
        {
            Expression<Func<SysAdminEntity, bool>> where = null;
            if (!string.IsNullOrEmpty(reqQueryAdminPage.Account))
            {
                where = where.And(m => m.Account.Contains(reqQueryAdminPage.Account));
            }

            if (!string.IsNullOrEmpty(reqQueryAdminPage.NickName))
            {
                where = where.And(m => m.NickName.Contains(reqQueryAdminPage.NickName));
            }

            if (!string.IsNullOrEmpty(reqQueryAdminPage.Phone))
            {
                where = where.And(m => m.Phone.Contains(reqQueryAdminPage.Phone));
            }

            if (!string.IsNullOrEmpty(reqQueryAdminPage.Email))
            {

                where = where.And(m => m.Email.Contains(reqQueryAdminPage.Email));
            }

            if (!string.IsNullOrEmpty(reqQueryAdminPage.WeChartAccount))
            {
                where = where.And(m => m.WeChartAccount.Contains(reqQueryAdminPage.WeChartAccount));
            }

            if (reqQueryAdminPage.Status != null)
            {
                where = where.And(m => m.Status == reqQueryAdminPage.Status);
            }

            var dbres = await _repository.GetPagesAsync(reqQueryAdminPage.PageSize, reqQueryAdminPage.PageNo, where, m => m.CreateTime);

            var resData = _mapper.Map<List<SysAdminEntity>, List<RepAdminPageInfo>>(dbres.Data);
            PageModel<RepAdminPageInfo>? res = new PageModel<RepAdminPageInfo>()
            {
                PageNo = dbres.PageNo,
                PageSize = dbres.PageSize,
                TotalCount = dbres.TotalCount,
                TotalPage = dbres.TotalPage,
                Data = resData
            };

            return res.GetResponse();
        }
        #endregion

        #region 普通登录

        /// <summary>
        /// 登录
        /// </summary>
        /// <param name="account"></param>
        /// <param name="pwd"></param> 
        /// <returns></returns>
        public async Task<ApiResult<string>> LoginAsync(string account, string pwd)
        {
            var res = new ApiResult<string>()
            {
                Code = StatusCodeEnum.Waring
            };

            #region 1. 判断用户登录次数限制以及过期时间

            //获得登录次数和过期时间
            var loginConfig = _cache.Get<CacheLoginLimit>(CacheKey.LoginCount + "-" + account) ?? new CacheLoginLimit();
            if (loginConfig.Account == account && loginConfig.Count == 4 && loginConfig.DelayMinute != null)
            {
                //说明存在过期时间，需要判断
                if (DateTime.Now <= loginConfig.DelayMinute)
                {
                    res.Msg = "您已连续登录失败超过3次，请稍后3分钟后再试";
                    return res;
                }
                else
                {
                    //已经过了登录的预设时间，重置登录配置参数 
                    loginConfig.Account = account;
                    loginConfig.Count = 0;
                    loginConfig.DelayMinute = null;
                }
            }
            #endregion

            #region 2.验证用户信息 

            var model = await _repository.GetModelAsync(m => m.Account == account && m.Pwd == pwd);
            if (model == null)
            {
                loginConfig.Count++;
                loginConfig.DelayMinute = DateTime.Now.AddMinutes(3);
                _cache.Set(CacheKey.LoginCount + "-" + account, loginConfig);

                res.Msg = "账户或密码错误";
                return res;
            }
            if (!model.Status.Value)
            {
                res.Msg = "账号已被禁用";
                return res;
            }

            #endregion

            #region 4. 生成token信息，并且返回给前端

            var _appConfig = AppsettingsUtils.Get<AppConfig>();

            var token = _tokenService.IssueToken(new TokenModel()
            {
                Id = model.Id,
                NickName = model.NickName,
                Account = model.Account,
                RoleIds = string.Join(",", "roles"),
                ProjectName = "EngineeringAPI",
                Issuer = _appConfig.JwtConfig.Issuer,
                Audience = _appConfig.JwtConfig.Audience,
                Expires = _appConfig.JwtConfig.Expires,
                SecretKey = _appConfig.JwtConfig.SecurityKey
            });

            //清楚缓存
            _cache.Del(CacheKey.LoginCount);

            #endregion

            #region 5. 修改用户登录信息

            model.LoginTotalCount += 1;
            model.LastLoginIp = _httpContextAccessor.HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString();
            model.LastLoginTime = DateTime.Now;
            await _repository.UpdateAsync(model);

            #endregion

            res.Data = token;
            res.Code = StatusCodeEnum.OK;

            return res;
        }

        /// <summary>
        /// 刷新token
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns> 
        public async Task<ApiResult<string>> RefreshTokenAsync(string token)
        {
            string jwtStr = string.Empty;

            if (string.IsNullOrEmpty(token))
            {
                return new ApiResult<string>()
                {
                    Code = StatusCodeEnum.Unauthorized,
                    Msg = "token无效，请重新登录！",
                };
            }
            var tokenModel = _tokenService.SerializeToken(token);
            if (tokenModel != null && !string.IsNullOrEmpty(tokenModel.Id))
            {
                var admin = await _repository.GetModelAsync(m => m.Id == tokenModel.Id);
                if (admin != null)
                {
                    var refreshToken = _tokenService.IssueToken(tokenModel);
                    return new ApiResult<string>()
                    {
                        Msg = "获取成功",
                        Data = refreshToken
                    };
                }
            }

            return new ApiResult<string>()
            {
                Code = StatusCodeEnum.Unauthorized,
                Msg = "认证失败！",
            };
        }

        #endregion

        #region 微信小程序登录
        /// <summary>
        /// 登录
        /// </summary>
        /// <param name="code">用户的登录凭证 code</param> 
        /// <returns></returns>
        public async Task<ApiResult<string>> WxLoginAsync(string code)
        {
            var res = new ApiResult<string>()
            {
                Code = StatusCodeEnum.Waring
            };

            #region 1. 判断基础配置信息

            // 这里可以添加日志记录，方便后续排查问题
            Console.WriteLine($"Received login code: {code}");

            var appconfig = await _cache.GetAsync<AppConfig>("APP_CONFIG");
            if (appconfig == null || appconfig.WxConfig == null || string.IsNullOrEmpty(appconfig.WxConfig?.Appid) || string.IsNullOrEmpty(appconfig.WxConfig?.AppSecret))
            {
                res.Msg = "系统暂未配置微信小程序相关配置项";
                return res;
            }
            #endregion

            var accessToken = await _cache.GetAsync<string>("WX_ACCESSTOKEN");
            if (string.IsNullOrEmpty(accessToken))
            {
                string phone = string.Empty;
                try
                {
                    var tokenRes = await WeChatMiniProgramUtil.GetTokenAsync(appconfig.WxConfig.Appid, appconfig.WxConfig.AppSecret);
                    if (!tokenRes.Item2)
                    {
                        D.Util.UtilConsole.ConsoleUtil.Err($"获取小程序Token失败:{tokenRes.Item1}");
                        res.Msg = tokenRes.Item1;
                        return res;
                    }
                    D.Util.UtilConsole.ConsoleUtil.Err($"获取小程序Token成功:{tokenRes.Item1}");

                    var phoneRes = await WeChatMiniProgramUtil.GetPhoneAsync(tokenRes.Item1, code);
                    if (!phoneRes.Item2)
                    {
                        D.Util.UtilConsole.ConsoleUtil.Err($"获取小程序手机号失败:{phoneRes.Item1}");
                        res.Msg = phoneRes.Item1;
                        return res;
                    }
                    D.Util.UtilConsole.ConsoleUtil.Err($"获取小程序手机号成功:{phoneRes.Item1}");
                    phone = phoneRes.Item1;
                }
                catch (Exception ex)
                {
                    D.Util.UtilConsole.ConsoleUtil.Err($"获取小程序用户信息异常:{ex.StackTrace}--{ex.Message}");
                    _nlogService.Error($"获取小程序用户信息异常:{ex.StackTrace}--{ex.Message}");
                    throw ex;
                }

                #region 2.验证用户信息 

                var model = await _repository.GetModelAsync(m => m.Phone == phone);
                if (model == null)
                {
                    res.Msg = "用户不存在";
                    return res;
                }

                if (!model.Status.Value)
                {
                    res.Msg = "账号已被禁用";
                    return res;
                }

                #endregion

                #region 4. 生成token信息，并且返回给前端

                var token = _tokenService.IssueToken(new TokenModel()
                {
                    Id = model.Id,
                    NickName = model.NickName,
                    Account = model.Account,
                    RoleIds = string.Join(",", "roles"),
                    ProjectName = "CozeWebApi",
                    Issuer = appconfig.JwtConfig.Issuer,
                    Audience = appconfig.JwtConfig.Audience,
                    Expires = appconfig.JwtConfig.Expires,
                    SecretKey = appconfig.JwtConfig.SecurityKey
                });

                //清楚缓存
                _cache.Del(CacheKey.LoginCount);

                #endregion

                #region 5. 修改用户登录信息

                model.LoginTotalCount += 1;
                model.LastLoginIp = _httpContextAccessor.HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString();
                model.LastLoginTime = DateTime.Now;
                await _repository.UpdateAsync(model);

                #endregion

                res.Data = token;
                res.Code = StatusCodeEnum.OK;

            }
            return res;
        }

        #endregion
    }
}